﻿using System;
using System.Collections;
using System.Drawing;
using System.Windows.Forms;

namespace TestAnimation
{
    public class TestStart : Form
    {
        enum GameState { PAUSED, TITLE, GAME, EXIT, GAMEOVER };
        private GameState gameState;
        private Screen screen;
        private readonly ArrayList bullets;
        private readonly ArrayList explosions;
        private readonly ArrayList particles;
        private Ship ship;
        private readonly ArrayList asteroids;
        private Ufo ufo;
        private bool bLeftPressed;
        private bool bRightPressed;
        private bool bSpaceLastPressed;
        private bool bUpPressed;
        private BufferedGraphicsContext context;
        private BufferedGraphics grafx;
        private Timer timer1;
        private int score;
        private int nbAsteroids;
        private Timer timer2;


        public TestStart()
        {
            Helper.InitialiseRandom();
            InitialiseWindowsObjects();
            particles = new ArrayList();
            explosions = new ArrayList();
            bullets = new ArrayList();
            asteroids = new ArrayList();
            screen=new Screen(grafx.Graphics, ClientRectangle);
            ship = new Ship();

            NewLevel(20,0);
            gameState = GameState.TITLE;
        }

        private void InitialiseWindowsObjects()
        {
            // Configure the Form for this example.
            Text = "Asteroids";

            AutoScaleBaseSize = new Size(5, 13);
            ClientSize = new Size(632, 453);
            Resize += OnResize;
            SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint, true);
            SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
            KeyDown += myKeyDown;
            KeyUp += myKeyUp;
            ResumeLayout(false);

            // Configure a timer to draw graphics updates.
            timer1 = new Timer {Interval = (1000/Globals.FPS), Enabled = true};
            timer1.Tick += OnTimer;

            timer2 = new Timer { Interval = 1, Enabled = false };
            timer2.Tick += DelegateCreateAsteroids;

            // Retrieves the BufferedGraphicsContext for the 
            // current application domain.
            context = BufferedGraphicsManager.Current;

            // Sets the maximum size for the primary graphics buffer
            // of the buffered graphics context for the application
            // domain.  Any allocation requests for a buffer larger 
            // than this will create a temporary buffered graphics 
            // context to host the graphics buffer.
            context.MaximumBuffer = new Size(Width + 1, Height + 1);

            // Allocates a graphics buffer the size of this form
            // using the pixel format of the Graphics created by 
            // the Form.CreateGraphics() method, which returns a 
            // Graphics object that matches the pixel format of the form.
            grafx = context.Allocate(CreateGraphics(),
                                     ClientRectangle);

            timer1.Start();
        }

        private void NewLevel(int n, int time)
        {
            nbAsteroids = n;

            timer2.Interval = time+1;
            timer2.Enabled = true;


        }
        private void DelegateCreateAsteroids(object sender, EventArgs e)
        {
            Asteroid ast;
            asteroids.Clear();
            timer2.Enabled=false;
            for (int i = 0; i < nbAsteroids; i++)
            {
                do
                {
                    ast = new Asteroid(1200);
                } while ((ship.Position - ast.Position).Magnitude() < 3000);
                asteroids.Add(ast);
            }
        }

        private void myKeyDown(object sender, KeyEventArgs e)
        {
            switch (gameState)
            {
                case GameState.PAUSED:
                    break;
                case GameState.TITLE:
                    if ((e.KeyData == Keys.Return))
                    {
                        ResetKeyboard();
                        particles.Clear();
                        explosions.Clear();
                        bullets.Clear();
                        NewLevel(4,0);
                        ufo = null;
                        score = 0;
                        ship = new Ship();
                        gameState = GameState.GAME;
                    }
                    if (e.KeyData == Keys.Escape)
                    {
                        Application.Exit();
                    }
                    break;
                case GameState.GAME:
                    if (e.KeyData == Keys.Up)       // Thrust
                    {
                        bUpPressed = true;
                    }
                    if (e.KeyData == Keys.Left)
                    {
                        bLeftPressed = true;
                    }
                    if (e.KeyData == Keys.Right)
                    {
                        bRightPressed = true;
                    }

                    if ((e.KeyData == Keys.Space) && !bSpaceLastPressed)
                    {
                        if (bullets.Count < Globals.maxBullets)
                        {
                            bSpaceLastPressed = true;
                            bullets.Add(new Bullet(ship));
                        }
                    }
                    if (e.KeyData == Keys.Escape)
                    {
                        NewLevel(7,0);
                        gameState = GameState.TITLE;
                    }

                    break;
                case GameState.EXIT:
                    break;
                case GameState.GAMEOVER:
                    if (e.KeyData == Keys.Escape || e.KeyData == Keys.Return)
                    {
                        NewLevel(7,0);
                        gameState = GameState.TITLE;
                    }
                    break;
                default:
                    throw new ArgumentOutOfRangeException();
            } 
        }

        private void myKeyUp(object sender, KeyEventArgs e)
        {
            if (gameState == GameState.GAME)
            {
                if (e.KeyData == Keys.Up) // Thrust
                {
                    bUpPressed = false;
                }
                if (e.KeyData == Keys.Left)
                {
                    bLeftPressed = false;
                }
                if (e.KeyData == Keys.Right)
                {
                    bRightPressed = false;
                }
                if (e.KeyData == Keys.Space)
                {
                    bSpaceLastPressed = false;
                }
            }
        }

        private void OnTimer(object sender, EventArgs e)
        {
            switch (gameState)
            {
                case GameState.PAUSED:
                    break;
                case GameState.TITLE:
                    DoAsteroids();
                    break;
                case GameState.GAME:
                    DoShip();
                    DoParticles();
                    DoAsteroids();
                    DoUfo();
                    DoCollisions();
                    DoBullets();
                    DoExplosions();
                    break;
                case GameState.GAMEOVER:
                    DoParticles();
                    DoAsteroids();                    
                    DoBullets();
                    DoUfo();
                    DoExplosions();
                    break;
                case GameState.EXIT:
                    break;
                default:
                    throw new ArgumentOutOfRangeException();
            }

            screen.Draw();
            DoText();
            Refresh();
        }
        
        private void DoParticles()
        {
            for (int i = 0; i < particles.Count; i++)
            {
                if (((Particle) particles[i]).Move())
                    ((Particle) particles[i]).Draw(screen);
                else
                    particles.RemoveAt(i);
            }
        }

        private void DoExplosions()
        {
            for (int i = 0; i < explosions.Count; i++)
            {
                if (((Explosion) explosions[i]).Draw(screen))
                    explosions.RemoveAt(i);
            }
        }
        private void DoUfo()
        {
            if(ufo==null) return;
            ufo.Move(ship.position);
            ufo.TransformCoordinates();
            if (ship != null)
            {
                Bullet bullet = ufo.Shoot(ship.position);
                if (bullet != null) bullets.Add(bullet);
            }
            ufo.Draw(screen);

        }

        private void DoBullets()
        {
            for (int i = 0; i < bullets.Count; i++)
            {
                ((Bullet) bullets[i]).Draw(screen);
                if (!((Bullet) bullets[i]).Move())
                {
                    bullets.RemoveAt(i);
                    break;
                }
            }
        }

        private void DoCollisions()
        {
            //Collisions asteroids with the ship
            for (int i = 0; i < asteroids.Count; i++)
            {
                foreach (var p in ship.TransformedPoints)
                {
                    if (((Asteroid) asteroids[i]).Collide((Point) p))
                    {
                        explosions.Add(new Explosion(ship));
                        BreakAsteroid(i);
                        if (asteroids.Count == 0) NewLevel(++nbAsteroids, 3000);
                        ship.energy -= 25;
                        if (ship.energy <= 0) gameState = GameState.GAMEOVER;
                        return;
                    }
                }
            }
            //Collisions asteroids with the bullets
            for (int i = 0; i < asteroids.Count; i++)
            {
                foreach (Bullet bullet in bullets)
                {
                    var p = new Point((int) bullet.position.X, (int) bullet.position.Y);
                    if (((Asteroid) asteroids[i]).Collide(p))
                    {
                        BreakAsteroid(i);
                        bullets.Remove(bullet);
                        if (asteroids.Count == 0) NewLevel(++nbAsteroids, 3000);
                        if (asteroids.Count < 5 && ufo == null) ufo=new Ufo();
                        return;
                    }
                }
            }
            //Collisions ship with bullets
            foreach (Bullet bullet in bullets)
            {
                var p = new Point((int)bullet.position.X, (int)bullet.position.Y);

                if (bullet.bulletType == Bullet.BulletType.BAD)
                {
                    if (ship.Collide(p))
                    {
                        explosions.Add(new Explosion(ship));
                        ship.energy -= 25;
                        bullets.Remove(bullet);
                        if (ship.energy <= 0) gameState = GameState.GAMEOVER;
                        return;
                    }
                }
            }
            
            //Collisions ufo with bullets
            if (ufo != null)
            {
                foreach (Bullet bullet in bullets)
                {
                    var p = new Point((int) bullet.position.X, (int) bullet.position.Y);

                    if (bullet.bulletType == Bullet.BulletType.GOOD)
                    {
                        if (ufo.Collide(p))
                        {
                            explosions.Add(new Explosion(ufo));
                            score += 500;
                            ufo = null;
                            bullets.Remove(bullet);

                            return;
                        }
                    }
                }
                //Collisions ship with ufo
                foreach (var p in ship.TransformedPoints)
                {
                    if (ufo.Collide((Point)p))
                    {
                        explosions.Add(new Explosion(ship));
                        explosions.Add(new Explosion(ufo));
                        ufo = null;
                        score += 500;
                        ship.energy -= 25;
                        if (ship.energy <= 0) gameState = GameState.GAMEOVER;
                        return;
                    }
                }
                //Collisions asteroids with the ufo
                for (int i = 0; i < asteroids.Count; i++)
                {
                    foreach (var p in ufo.TransformedPoints)
                    {
                        if (((Asteroid)asteroids[i]).Collide((Point)p))
                        {
                            explosions.Add(new Explosion(ufo));
                            BreakAsteroid(i);
                            if (asteroids.Count == 0) NewLevel(++nbAsteroids, 3000);
                            ufo = null;
                            score += 500;
                            return;
                        }
                    }
                }
            }

        }
        private void ResetKeyboard()
        {
            bLeftPressed=bRightPressed=bSpaceLastPressed=bUpPressed=false;
        }

        private void BreakAsteroid(int i)
        {
            score += 100;
            explosions.Add(new Explosion(((Asteroid)asteroids[i])));
            switch (((Asteroid) asteroids[i]).Size)
            {
                case 1200:
                    var k = Helper.GetRnd(2) + 2;
                    for (var m = 0; m < k; m++)
                        asteroids.Add(new Asteroid(600, ((Asteroid) asteroids[i]).Position));
                    break;
                case 600:
                    var l = Helper.GetRnd(4) + 2;
                    for (var m = 0; m < l; m++)
                        asteroids.Add(new Asteroid(300, ((Asteroid)asteroids[i]).Position));
                    break;
            }
            asteroids.RemoveAt(i);
        }

        private void DoAsteroids()
        {
            for (int i = 0; i < asteroids.Count; i++)
            {
                ((Asteroid) asteroids[i]).Move();
                ((Asteroid) asteroids[i]).TransformCoordinates();
                ((Asteroid)asteroids[i]).Draw(screen);
            }
        }

        private void DoShip()
        {
            ship.Move();
            if (bUpPressed)
            {
                ship.Thrust();
                particles.Add(ship.NewParticle());
                particles.Add(ship.NewParticle());
            }
            if (bLeftPressed)
                ship.ObjectOrientation -= 5;
            if (bRightPressed)
                ship.ObjectOrientation += 5;


            ship.TransformCoordinates();
            ship.Draw(screen);
        }

        private void DoText()
        {
            string msgScore = "SCORE: " + score + "     SHIELD: " + ship.energy + "%";
            string msgCenter = "";
            var rect=new RectangleF(Globals.maxX/4f,Globals.maxY/2-500,Globals.maxX/2f,1000);
            var format1 = new StringFormat(StringFormatFlags.NoClip)
                              {
                                  LineAlignment = StringAlignment.Near,
                                  Alignment = StringAlignment.Center
                              };


            switch (gameState)
            {
                case GameState.PAUSED:
                    break;
                case GameState.TITLE:
                    msgCenter = "PRESS RETURN TO PLAY";
                    break;
                case GameState.GAME:
                    break;
                case GameState.EXIT:
                    break;
                case GameState.GAMEOVER:
                    msgCenter = "GAME OVER";

                    break;
                default:
                    throw new ArgumentOutOfRangeException();
            }
            grafx.Graphics.DrawString(msgCenter,
                    new Font("Arial", 160),
                    new SolidBrush(Color.Red),
                    rect, format1);
            grafx.Graphics.DrawString(msgScore,
                                      new Font("Arial", 160),
                                      new SolidBrush(Color.Red),
                                      0, 0);            
        }

        private void OnResize(object sender, EventArgs e)
        {
            // Re-create the graphics buffer for a new window size.
            context.MaximumBuffer = new Size(Width + 1, Height + 1);
            if (grafx != null)
            {
                grafx.Dispose();
                grafx = null;
            }
            grafx = context.Allocate(CreateGraphics(),
                                     ClientRectangle);
            screen = new Screen(grafx.Graphics, ClientRectangle);
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            grafx.Render(e.Graphics);
        }

        [STAThread]
        public static void Main(string[] args)
        {
            Application.Run(new TestStart());
        }
    }
}